/********************************************************************************
 *                               libemu
 *
 *                    - x86 shellcode emulation -
 *
 *
 * Copyright (C) 2007  Paul Baecher & Markus Koetter
 * 
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 * 
 * 
 *             contact nepenthesdev@users.sourceforge.net  
 *
 *******************************************************************************/

#ifndef HAVE_EMU_H
#define HAVE_EMU_H

#include "../../../include/incface.h"
#include <emu_cpu_instruction.h>
#include <emu_instruction.h>

#ifndef MIN
	#define        MIN(a,b) (((a)<(b))?(a):(b))
#endif

#ifndef MAX
	#define        MAX(a,b) (((a)>(b))?(a):(b))
#endif

#include <memory.h>

#pragma warning( disable : 4996 ) //unsafe function (strcpy,vsnprintf)
#pragma warning( disable : 4018 ) //signed unsigned mismatch

#define BYTE_ORDER	4321

enum emu_reg32 {
	eax = 0, ecx, edx, ebx, esp, ebp, esi, edi
};

enum emu_reg16
{
	ax =  0,/* eax */
	cx, 	/* ecx */
	dx, 	/* edx */
	bx, 	/* ebx */
	sp, 	/* esp */
	bp, 	/* ebp */
	si, 	/* esp */
	di  	/* edi */
};

enum emu_reg8
{
	al=0,  	/* eax */
	cl,    	/* ecx */
	dl,    	/* edx */
	bl,    	/* ebx */
	ah,     /* eax */
	ch,     /* ecx */
	dh,     /* edx */
	bh      /* ebx */
};

enum emu_seg
{
	s_cs = 0, s_ss, s_ds, s_es, s_fs, s_gs, s_max
};

enum emu_macc
{
	mem_read	= 0x1,
	mem_write	= 0x2,
	mem_exec	= 0x4,

	mem_moa		= 0x10,		// map on access
	mem_hka		= 0x20,		// hook all access
	mem_zpg		= 0x40,		// zero page
	mem_wcpy	= 0x80,

	mem_priv	= 0x100,
	mem_map		= 0x200,
	mem_img		= 0x400,
	mem_const	= 0x800,

	mem_host	= 0x1000,
};

//////////////////////////////////////////////////////////////////////////
//
//	Memory
//

#define PAGE_BITS 12 /* size of one page, 2^12 = 4096 */
#define PAGESET_BITS 10 /* number of pages in one pageset, 2^10 = 1024 */

#pragma warning (disable:4311)
#pragma warning (disable:4312)

#define PAGE_SIZE (1 << PAGE_BITS)
#define PAGESET_SIZE (1 << PAGESET_BITS)

#define PAGESET(x) ((x) >> (PAGESET_BITS + PAGE_BITS))
#define PAGE(x) (((x) >> PAGE_BITS) & ((1 << PAGESET_BITS) - 1))
#define OFFSET(x) (((1 << PAGE_BITS) - 1) & (x))

#define EMU_PAGE_SIZE		0x1000
#define PAGE_BASE_MASK		0xFFFFF000
#define PAGE_OFFSET_MASK	0x00000FFF
#define PAGE_SHIFT			12
#define MAX_PAGES_IN_4G		0x000FFFFF
#define MAX_PAGES_IN_2G		0x0007FFFF
#define PAGE_ALIGN_BIG(x)	(x/EMU_PAGE_SIZE)+(

#define emu_strerror_set()
#define logDebug()

long	emu_cpu_errno_set(emu_cpu *c, long err);
long	emu_cpu_errno_get(emu_cpu *c);

inline uint32_t aligned_page_count( uint32_t addr )
{
	return ((addr&PAGE_BASE_MASK)>>PAGE_SHIFT) + ((addr & PAGE_OFFSET_MASK)?1:0);
}

struct  emu_segdesc
{
	uint32_t	pgbase;
	uint32_t	pgcount;
};

struct emu_context
{
	uint32_t	preveip;
	uint32_t	eip;
	uint32_t	eflags;
	uint32_t	reg[8];
	uint16_t *	reg16[8];
	uint8_t *	reg8[8];
	uint16_t	segr[s_max];
	emu_segdesc	segments[s_max];
	uint32_t	cr2;
	uint32_t	excep_err_code;
};

struct emu_exec_inst : emu_instruction
{
	emu_exec_inst();
	emu_cpu_instruction_info *	info_;
	void fix() { emu_instruction::fix(); };
	void reset() { emu_instruction::reset(); info_= 0; };
	void clone_from( emu_exec_inst * p ) { memcpy( this, p, sizeof(*this) ); fix(); };
};

struct emu_exec_page 
{
	uint32_t	addr;
};

struct i_emu_mem
{
	virtual long access_byte( emu_cpu *c, uint32_t guest, size_t acc, void * byte ) = 0;
	virtual long access_word( emu_cpu *c, uint32_t guest, size_t acc, void * byte ) = 0;
	virtual long access_dword( emu_cpu *c, uint32_t guest, size_t acc, void * byte ) = 0;
	virtual long access_bytes( emu_cpu *c, uint32_t guest, size_t acc, void * byte, size_t cnt, size_t * eff ) = 0;
	virtual emu_exec_inst * icache_get( emu_cpu * c, uint32_t guest ) = 0;
};

struct emu_cpu;

struct inst_cache_t
{
	enum { SLOTS = 0x8000 };
	struct item_t
	{
		uint32_t		guest;
		emu_exec_inst	inst;
		item_t() : guest(0) {};
	};
	item_t	_items[SLOTS];
	emu_exec_inst * get( uint32_t guest );
	void invalid( uint32_t guest, size_t bytes );
};

struct emu_cpu : emu_context
{
	i_emu_mem *					mem;
	long						errcode;
	//////////////////////////////////////////////////////////////////////////
	bool						in_repeat_inst;
	uint32_t					debugflags;
	//////////////////////////////////////////////////////////////////////////
	emu_seg						current_segment;
	//////////////////////////////////////////////////////////////////////////
	emu_exec_inst				inst_;
	uint32_t					last_fpu_instr[2];
	//////////////////////////////////////////////////////////////////////////
public:
	emu_cpu( i_emu_mem * pmio )
	{
		memset( this, 0, sizeof(*this) );
		mem = pmio;
		init__();
	}
public:
	inline emu_context& context() { return *this; };
public:
	long	execute1();
	size_t	execute( size_t steps );
	long	execute_to( uint32_t eip, size_t max_steps, size_t * executed = 0 );
	long	reexecute();
	long	set_segment( emu_seg seg, uint32_t base, uint32_t limit );
	long	convert_to_virtual_address( emu_seg seg, uint32_t * guest, uint32_t bytes = 0 );
	long	push( uint32_t val );
protected:
	void	init__();
	long	fetch_instruction();
	long	step();
	long	select_segment( emu_seg segi );
};

inline emu_seg emu_cpu_current_segment( emu_cpu * c) { return c->current_segment; };
inline emu_seg emu_cpu_select_segment(emu_cpu * c, emu_seg seg) { emu_seg r = c->current_segment; c->current_segment = seg; return r;}; 
inline uint16_t emu_cpu_get_seg_reg( emu_cpu * c, emu_seg seg ) { return c->segr[seg]; };
inline void emu_cpu_set_seg_reg( emu_cpu * c, emu_seg seg, uint32_t val ) { c->segr[seg] = (uint16_t)val; }
//////////////////////////////////////////////////////////////////////////
inline void* bcopy (void* src, void* dest, unsigned int len){ return memcpy(dest, src, len ); };
#endif // HAVE_EMU_H
